import XEUtils from 'xe-utils'
import PropTypes from 'ant-design-vue/es/_util/vue-types'
import {JVXETypes} from '@/components/jeecg/JVxeTable/jvxeTypes'
import VxeWebSocketMixins from '../mixins/vxe.web.socket.mixins'
import {initDictOptions} from '@/components/dict/JDictSelectUtil'

import {getRefPromise} from '../utils/vxeUtils'
import {getEnhancedMixins, replaceProps} from '../utils/cellUtils'

import JVxeToolbar from './JVxeToolbar'
import JVxeSubPopover from './JVxeSubPopover'
import JVxeDetailsModal from './JVxeDetailsModal'
import JVxePagination from './JVxePagination'
import {cloneObject, getVmParentByName, pushIfNotExist, randomString, simpleDebounce} from '@/utils/util'
import {UtilTools} from 'vxe-table/packages/tools/src/utils'
import {getNoAuthCols} from '@/utils/authFilter'

export default {
    name: 'JVxeTable',
    provide() {
        return {
            superTrigger: (name, event) => this.trigger(name, event)
        }
    },
    mixins: [VxeWebSocketMixins],
    components: {JVxeToolbar, JVxeSubPopover, JVxeDetailsModal, JVxePagination},
    props: {
        rowKey: PropTypes.string.def('id'),
        // 列信息
        columns: {
            type: Array,
            required: true
        },
        // 数据源
        dataSource: {
            type: Array,
            required: true
        },
        authPre: {
            type: String,
            required: false,
            default: ''
        },
        // 是否显示工具栏
        toolbar: PropTypes.bool.def(false),
        // 工具栏配置
        toolbarConfig: PropTypes.object.def(() => ({
            // prefix 前缀；suffix 后缀；
            slots: ['prefix', 'suffix'],
            // add 新增按钮；remove 删除按钮；clearSelection 清空选择按钮；collapse 展开收起
            btns: ['add', 'remove', 'clearSelection'],
        })),
        // 是否显示行号
        rowNumber: PropTypes.bool.def(false),
        // 是否可选择行
        rowSelection: PropTypes.bool.def(false),
        // 选择行类型
        rowSelectionType: PropTypes.oneOf(['checkbox', 'radio']).def('checkbox'),
        // 是否可展开行
        rowExpand: PropTypes.bool.def(false),
        // 展开行配置
        expandConfig: PropTypes.object.def(() => ({})),
        // 页面是否在加载中
        loading: PropTypes.bool.def(false),
        height: PropTypes.instanceOf([Number, String]).def('auto'),
        // 最大高度
        maxHeight: {
            type: Number,
            default: () => null,
        },
        // 要禁用的行 TODO 未实现
        disabledRows: PropTypes.object.def(() => ({})),
        // 是否禁用全部组件
        disabled: PropTypes.bool.def(false),
        // 是否可拖拽排序 TODO 仅实现上下排序，未实现拖拽排序（可能无法实现或较为困难）
        dragSort: PropTypes.bool.def(false),
        // 排序字段保存的Key
        dragSortKey: PropTypes.string.def('orderNum'),
        // 大小，可选值有：medium（中）、small（小）、mini（微）、tiny（非常小）
        size: PropTypes.oneOf(['medium', 'small', 'mini', 'tiny']).def('medium'),
        // 是否显示边框线
        bordered: PropTypes.bool.def(false),
        // 分页器参数，设置了即可显示分页器
        pagination: PropTypes.object.def(() => ({})),
        // 点击行时是否显示子表单
        clickRowShowSubForm: PropTypes.bool.def(false),
        // 点击行时是否显示主表单
        clickRowShowMainForm: PropTypes.bool.def(false),
        // 是否点击选中行，优先级最低
        clickSelectRow: PropTypes.bool.def(false),
        // 是否开启 reload 数据效果
        reloadEffect: PropTypes.bool.def(false),
        // 校验规则
        editRules: PropTypes.object.def(() => ({})),
        // 是否异步删除行，如果你要实现异步删除，那么需要把这个选项开启，
        // 在remove事件里调用confirmRemove方法才会真正删除（除非删除的全是新增的行）
        asyncRemove: PropTypes.bool.def(false),
        // 是否一直显示组件，如果为false则只有点击的时候才出现组件
        // 注：该参数不能动态修改；如果行、列字段多的情况下，会根据机器性能造成不同程度的卡顿。
        alwaysEdit: PropTypes.bool.def(false),
        // 联动配置，数组，详情配置见文档
        linkageConfig: PropTypes.array.def(() => []),
    },
    data() {
        return {
            isJVXETable: true,
            // caseId，表格唯一标识
            caseId: `_j-vxe-${randomString(8)}_`,
            // 内置columns
            _innerColumns: [],
            // 内置 EditRules
            _innerEditRules: [],
            // 记录滚动条位置
            scroll: {top: 0, left: 0},
            // 当前是否正在滚动
            scrolling: false,
            // vxe 默认配置
            defaultVxeProps: {
                'row-id': this.rowKey,
                // 高亮hover的行
                'highlight-hover-row': true,
                // 溢出隐藏并显示tooltip
                'show-overflow': true,
                // 表头溢出隐藏并显示tooltip
                'show-header-overflow': true,
                'show-footer-overflow': true,
                // 可编辑配置
                'edit-config': {trigger: 'click', mode: 'cell', showStatus: true},
                'expand-config': {
                    iconClose: 'ant-table-row-expand-icon ant-table-row-collapsed',
                    iconOpen: 'ant-table-row-expand-icon ant-table-row-expanded'
                },
                // 虚拟滚动配置，y轴大于30条数据时启用虚拟滚动
                // 'scroll-y': {
                //   gt: 30
                // },
                // 'scroll-x': {
                //   gt: 15
                // },
                'radio-config': {highlight: true},
                'checkbox-config': {highlight: true},
            },
            // 绑定左侧选择框
            selectedRows: [],
            // 绑定左侧选择框已选择的id
            selectedRowIds: [],
            // 统计列配置
            statistics: {
                has: false,
                sum: [],
                average: [],
            },
            // 允许执行刷新特效的行ID
            reloadEffectRowKeysMap: {},
            //配置了但是没有授权的按钮和列 集合
            excludeCode: [],
            // 联动下拉选项（用于隔离不同的下拉选项）
            // 内部联动配置，map
            _innerLinkageConfig: null,
        }
    },
    computed: {

        // vxe 最终 columns
        vxeColumns() {
            this._innerColumns.forEach(column => {
                let renderOptions = {
                    caseId: this.caseId,
                    bordered: this.bordered,
                    disabled: this.disabled,
                    scrolling: this.scrolling,
                    reloadEffect: this.reloadEffect,
                    reloadEffectRowKeysMap: this.reloadEffectRowKeysMap,
                    listeners: this.cellListeners,
                }
                if (column.$type === JVXETypes.rowDragSort) {
                    renderOptions.dragSortKey = this.dragSortKey
                }
                // slot 组件特殊处理
                if (column.$type === JVXETypes.slot) {
                    if (this.$scopedSlots.hasOwnProperty(column.slotName)) {
                        renderOptions.slot = this.$scopedSlots[column.slotName]
                        renderOptions.target = this
                    }
                }
                // 处理联动列，联动列只能作用于 select 组件
                if (column.$type === JVXETypes.select && this._innerLinkageConfig != null) {
                    // 判断当前列是否是联动列
                    if (this._innerLinkageConfig.has(column.key)) {
                        renderOptions.linkage = {
                            config: this._innerLinkageConfig.get(column.key),
                            getLinkageOptionsSibling: this.getLinkageOptionsSibling,
                            getLinkageOptionsAsync: this.getLinkageOptionsAsync,
                            linkageSelectChange: this.linkageSelectChange,
                        }
                    }
                }
                if (column.editRender) {
                    Object.assign(column.editRender, renderOptions)
                }
                if (column.cellRender) {
                    Object.assign(column.cellRender, renderOptions)
                }
                // update--begin--autor:lvdandan-----date:20201019------for:LOWCOD-882 【新行编辑】列表上带按钮的遮挡问题
                if (column.$type === JVXETypes.file || column.$type === JVXETypes.image) {
                    if (column.width && column.width.endsWith('px')) {
                        column.width = Number.parseInt(column.width.substr(0, column.width.length - 2)) + Number.parseInt(1) + 'px';
                    }
                }
                // update--begin--autor:lvdandan-----date:20201019------for:LOWCOD-882 【新行编辑】列表上带按钮的遮挡问题

                // update--begin--autor:lvdandan-----date:20201211------for:JT-118 【online】 日期、时间控件长度较小
                if (column.$type === JVXETypes.datetime || column.$type === JVXETypes.userSelect || column.$type === JVXETypes.departSelect) {
                    let width = column.width && column.width.endsWith('px') ? Number.parseInt(column.width.substr(0, column.width.length - 2)) : 0;
                    if (width <= 190) {
                        column.width = '190px'
                    }
                }
                if (column.$type === JVXETypes.date) {
                    let width = column.width && column.width.endsWith('px') ? Number.parseInt(column.width.substr(0, column.width.length - 2)) : 0;
                    if (width <= 135) {
                        column.width = '135px'
                    }
                }
                // update--end--autor:lvdandan-----date:20201211------for:JT-118 【online】 日期、时间控件长度较小
            })
            return this._innerColumns
        },
        // vxe 最终 editRules
        vxeEditRules() {
            return Object.assign({}, this.editRules, this._innerEditRules)
        },
        // vxe 最终 props
        vxeProps() {
            let expandConfig = Object.assign({}, this.defaultVxeProps['expand-config'], this.expandConfig)

            return Object.assign({}, this.defaultVxeProps, {
                showFooter: this.statistics.has,
            }, this.$attrs, {
                loading: this.loading,
                columns: this.vxeColumns,
                editRules: this.vxeEditRules,
                // data: this.dataSource,
                height: this.height === 'auto' ? null : this.height,
                maxHeight: this.maxHeight,
                border: this.bordered,
                expandConfig: expandConfig,
                footerMethod: this.handleFooterMethod,
                // footerSpanMethod: this.handleFooterSpanMethod,
            })
        },
        // vxe 最终 events
        vxeEvents() {
            // 内置事件
            let events = {
                'scroll': this.handleVxeScroll,
                'cell-click': this.handleCellClick,
                'edit-closed': this.handleEditClosed,
                'edit-actived': this.handleEditActived,
                'radio-change': this.handleVxeRadioChange,
                'checkbox-all': this.handleVxeCheckboxAll,
                'checkbox-change': this.handleVxeCheckboxChange,
            }
            // 用户传递的事件，进行合并操作
            Object.keys(this.$listeners).forEach(key => {
                let listen = this.$listeners[key]
                if (events.hasOwnProperty(key)) {
                    if (Array.isArray(listen)) {
                        listen.push(events[key])
                    } else {
                        listen = [events[key], listen]
                    }
                }
                events[key] = listen
            })
            return events
        },
        // 组件监听事件
        cellListeners() {
            return {
                trigger: (name, event) => this.trigger(name, event),
                valueChange: event => this.trigger('valueChange', event),
                /** 当前行向上移一位 */
                rowMoveUp: rowIndex => this.rowResort(rowIndex, rowIndex - 1),
                /** 当前行向下移一位 */
                rowMoveDown: rowIndex => this.rowResort(rowIndex, rowIndex + 1),
                /** 在当前行下面插入一行 */
                rowInsertDown: rowIndex => this.insertRows({}, rowIndex + 1),
            }
        },
    },
    watch: {
        dataSource: {
            //   deep: true,
            immediate: true,
            async handler() {
                let vxe = await getRefPromise(this, 'vxe')

                this.dataSource.forEach((data, idx) => {
                    // 开启了排序就自动计算排序值
                    if (this.dragSort) {
                        this.$set(data, this.dragSortKey, idx + 1)
                    }
                    // 处理联动回显数据
                    if (this._innerLinkageConfig != null) {
                        for (let configItem of this._innerLinkageConfig.values()) {
                            this.autoSetLinkageOptionsByData(data, '', configItem, 0)
                        }
                    }
                })

                // 阻断vue监听大数据，提高性能
                vxe.loadData(this.dataSource)

                // TODO 解析disabledRows
                // let disabled = false
                //
                // let disabledRowIds = (this.disabledRowIds || [])
                // // 解析disabledRows
                // Object.keys(this.disabledRows).forEach(disabledColKey => {
                //   // 判断是否有该属性
                //   if (data.hasOwnProperty(disabledColKey)) {
                //     if (disabled !== true) {
                //       let temp = this.disabledRows[disabledColKey]
                //       // 禁用规则可以是一个数组
                //       if (Array.isArray(temp)) {
                //         disabled = temp.includes(data[disabledColKey])
                //       } else {
                //         disabled = (temp === data[disabledColKey])
                //       }
                //       if (disabled) {
                //         disabledRowIds.push(row.id)
                //       }
                //     }
                //   }
                // })
            },
        },
        columns: {
            immediate: true,
            handler(columns) {
                //获取不需要显示列
                this.loadExcludeCode()
                let _innerColumns = []
                let _innerEditRules = {}
                let {rowNumber, rowSelection, rowExpand, dragSort} = this
                let expandColumn, seqColumn, checkboxColumn, radioColumn, dragSortColumn
                if (Array.isArray(columns)) {
                    this.statistics.has = false
                    this.statistics.sum = []
                    this.statistics.average = []

                    // 处理成vxe可识别的columns
                    columns.forEach(column => {
                        if (this.excludeCode.indexOf(column.key) >= 0) {
                            return false
                        }
                        let col = {...column}
                        let {type} = col
                        const enhanced = getEnhancedMixins(type)
                        if (type === JVXETypes.rowNumber) {
                            seqColumn = col
                        } else if (type === JVXETypes.rowCheckbox) {
                            checkboxColumn = col
                        } else if (type === JVXETypes.rowRadio) {
                            radioColumn = col
                        } else if (type === JVXETypes.rowExpand) {
                            expandColumn = col
                        } else if (type === JVXETypes.rowDragSort) {
                            dragSortColumn = col
                        } else {
                            col.field = col.key
                            // 防止和vxeTable自带的type起冲突
                            col.$type = col.type
                            delete col.type
                            let renderName = 'cellRender', renderOptions = {name: JVXETypes._prefix + type}
                            if (type) {
                                // hidden 是特殊的组件
                                if (type === JVXETypes.hidden) {
                                    col.visible = false
                                } else if (enhanced.switches.editRender) {
                                    renderName = 'editRender'
                                    renderOptions.type = (enhanced.switches.visible || this.alwaysEdit) ? 'visible' : 'default'
                                }
                            } else {
                                renderOptions.name = JVXETypes._prefix + JVXETypes.normal
                            }
                            col[renderName] = renderOptions
                            // 处理字典
                            if (col.dictCode) {
                                this._loadDictConcatToOptions(col)
                            }
                            // 处理校验
                            if (col.validateRules) {
                                let rules = []
                                if (Array.isArray(col.validateRules)) {
                                    for (let rule of col.validateRules) {
                                        let replace = {
                                            message: replaceProps(col, rule.message)
                                        }
                                        if (rule.unique || rule.pattern === 'only') {
                                            // 唯一校验器
                                            rule.validator = uniqueValidator.bind(this)
                                        } else if (rule.pattern) {
                                            // 非空
                                            if (rule.pattern === fooPatterns[0].value) {
                                                rule.required = true
                                                delete rule.pattern
                                            } else {
                                                // 兼容Online表单的特殊规则
                                                for (let foo of fooPatterns) {
                                                    if (foo.value === rule.pattern) {
                                                        rule.pattern = foo.pattern
                                                        break
                                                    }
                                                }
                                            }
                                        } else if (typeof rule.handler === 'function') {
                                            // 自定义函数校验
                                            rule.validator = handlerConvertToValidator.bind(this)
                                        }
                                        rules.push(Object.assign({}, rule, replace))
                                    }
                                }
                                _innerEditRules[col.key] = rules
                            }
                            // 处理统计列
                            // sum = 求和、average = 平均值
                            if (Array.isArray(col.statistics)) {
                                this.statistics.has = true
                                col.statistics.forEach(item => {
                                    let arr = this.statistics[item.toLowerCase()]
                                    if (Array.isArray(arr)) {
                                        pushIfNotExist(arr, col.key)
                                    }
                                })
                            }
                            _innerColumns.push(col)
                        }
                    })
                }
                // 判断是否开启了序号
                if (rowNumber) {
                    let col = {type: 'seq', title: '#', width: 60, fixed: 'left', align: 'center'}
                    if (seqColumn) {
                        col = Object.assign(col, seqColumn, {type: 'seq'})
                    }
                    _innerColumns.unshift(col)
                }
                // 判断是否开启了可选择行
                if (rowSelection) {
                    let width = 40
                    if (this.statistics.has && !rowExpand && !dragSort) {
                        width = 60
                    }
                    let col = {type: this.rowSelectionType, width, fixed: 'left', align: 'center'}
                    // radio
                    if (this.rowSelectionType === 'radio' && radioColumn) {
                        col = Object.assign(col, radioColumn, {type: 'radio'})
                    }
                    // checkbox
                    if (this.rowSelectionType === 'checkbox' && checkboxColumn) {
                        col = Object.assign(col, checkboxColumn, {type: 'checkbox'})
                    }
                    _innerColumns.unshift(col)
                }
                // 是否可展开行
                if (rowExpand) {
                    let width = 40
                    if (this.statistics.has && !dragSort) {
                        width = 60
                    }
                    let col = {
                        type: 'expand',
                        title: '',
                        width,
                        fixed: 'left',
                        align: 'center',
                        slots: {content: 'expandContent'}
                    }
                    if (expandColumn) {
                        col = Object.assign(col, expandColumn, {type: 'expand'})
                    }
                    _innerColumns.unshift(col)
                }
                // 是否可拖动排序
                if (dragSort) {
                    let width = 40
                    if (this.statistics.has) {
                        width = 60
                    }
                    let col = {
                        type: JVXETypes.rowDragSort,
                        title: '',
                        width,
                        fixed: 'left',
                        align: 'center',
                        cellRender: {name: JVXETypes._prefix + JVXETypes.rowDragSort}
                    }
                    if (dragSortColumn) {
                        col = Object.assign(col, dragSortColumn, {type: JVXETypes.rowDragSort})
                    }
                    _innerColumns.unshift(col)
                }

                this._innerColumns = _innerColumns
                this._innerEditRules = _innerEditRules
            }
        },
        // watch linkageConfig
        // 整理多级联动配置
        linkageConfig: {
            immediate: true,
            handler() {
                if (Array.isArray(this.linkageConfig) && this.linkageConfig.length > 0) {
                    // 获取联动的key顺序
                    let getLcKeys = (key, arr) => {
                        let col = this._innerColumns.find(col => col.key === key)
                        if (col) {
                            arr.push(col.key)
                            if (col.linkageKey) {
                                return getLcKeys(col.linkageKey, arr)
                            }
                        }
                        return arr
                    }
                    let configMap = new Map()
                    this.linkageConfig.forEach(lc => {
                        let keys = getLcKeys(lc.key, [])
                        // 多个key共享一个，引用地址
                        let configItem = {
                            ...lc, keys,
                            optionsMap: new Map()
                        }
                        keys.forEach(k => configMap.set(k, configItem))
                    })
                    this._innerLinkageConfig = configMap
                } else {
                    this._innerLinkageConfig = null
                }
            }
        },
    },
    created() {
    },
    mounted() {
        this.handleTabsChange()
    },
    methods: {

        /**
         * 自动判断父级是否是 <a-tabs/> 组件，然后添加事件监听，自动重置表格
         */
        handleTabsChange() {
            // 获取父级
            const tabs = getVmParentByName(this, 'ATabs')
            const tabPane = getVmParentByName(this, 'ATabPane')
            if (tabs && tabPane) {
                // 用户自定义的 key
                const currentKey = tabPane.$vnode.key
                // 添加 activeKey 监听
                const unwatch = tabs.$children[0].$watch('$data._activeKey', async (key) => {
                    // 切换到自己时重新计算
                    if (currentKey === key) {
                        await this.$nextTick()
                        await this.refreshScroll()
                        await this.recalculate()
                    }
                })
                // 当前实例销毁时取消监听
                this.$on('beforeDestroy', () => unwatch())
            }
        },

        handleVxeScroll(event) {
            let {$refs, scroll} = this

            // 记录滚动条的位置
            scroll.top = event.scrollTop
            scroll.left = event.scrollLeft

            $refs.subPopover ? $refs.subPopover.close() : null
            this.scrolling = true
            this.closeScrolling()
        },
        // 当手动勾选单选时触发的事件
        handleVxeRadioChange(event) {
            let row = event.$table.getRadioRecord()
            this.selectedRows = row ? [row] : []
            this.handleSelectChange('radio', this.selectedRows, event)
        },
        // 当手动勾选全选时触发的事件
        handleVxeCheckboxAll(event) {
            this.selectedRows = event.$table.getCheckboxRecords()
            this.handleSelectChange('checkbox-all', this.selectedRows, event)
        },
        // 当手动勾选并且值发生改变时触发的事件
        handleVxeCheckboxChange(event) {
            this.selectedRows = event.$table.getCheckboxRecords()
            this.handleSelectChange('checkbox', this.selectedRows, event)
        },
        // 行选择change事件
        handleSelectChange(type, selectedRows, $event) {
            let action
            if (type === 'radio') {
                action = 'selected'
            } else if (type === 'checkbox') {
                action = selectedRows.includes($event.row) ? 'selected' : 'unselected'
            } else {
                action = 'selected-all'
            }

            this.selectedRowIds = selectedRows.map(row => row.id)
            this.trigger('selectRowChange', {
                type: type,
                action: action,
                $event: $event,
                row: $event.row,
                selectedRows: this.selectedRows,
                selectedRowIds: this.selectedRowIds
            })
        },

        // 点击单元格时触发的事件
        handleCellClick(event) {
            let {row, column, $event, $table} = event
            let {$refs} = this

            // 点击了可编辑的
            if (column.editRender) {
                $refs.subPopover ? $refs.subPopover.close() : null
                return
            }

            // 显示详细信息
            if (column.own.showDetails) {
                // 两个如果同时存在的话会出现死循环
                $refs.subPopover ? $refs.subPopover.close() : null
                $refs.detailsModal ? $refs.detailsModal.open(event) : null
            } else if ($refs.subPopover) {
                $refs.subPopover.toggle(event)
            } else if (this.clickSelectRow) {
                let className = $event.target.className || ''
                className = typeof className === 'string' ? className : className.toString()
                // 点击的是expand，不做处理
                if (className.includes('vxe-table--expand-btn')) {
                    return
                }
                // 点击的是checkbox，不做处理
                if (className.includes('vxe-checkbox--icon') || className.includes('vxe-cell--checkbox')) {
                    return
                }
                // 点击的是radio，不做处理
                if (className.includes('vxe-radio--icon') || className.includes('vxe-cell--radio')) {
                    return
                }
                if (this.rowSelectionType === 'radio') {
                    $table.setRadioRow(row)
                    this.handleVxeRadioChange(event)
                } else {
                    $table.toggleCheckboxRow(row)
                    this.handleVxeCheckboxChange(event)
                }
            }
        },

        // 单元格编辑状态下被关闭时会触发该事件
        handleEditClosed({column}) {
            // 执行增强
            getEnhancedMixins(column.own.$type, 'aopEvents').editClosed.apply(this, arguments)
        },

        // 单元格被激活编辑时会触发该事件
        handleEditActived({column}) {
            // 执行增强
            getEnhancedMixins(column.own.$type, 'aopEvents').editActived.apply(this, arguments)
        },

        /** 表尾数据处理方法，用于显示统计信息 */
        handleFooterMethod({columns, data}) {
            const {statistics} = this
            let footers = []
            if (statistics.has) {
                if (statistics.sum.length > 0) {
                    footers.push(this.getFooterStatisticsMap({
                        columns: columns,
                        title: '合计',
                        checks: statistics.sum,
                        method: (column) => XEUtils.sum(data, column.property)
                    }))
                }
                if (statistics.average.length > 0) {
                    footers.push(this.getFooterStatisticsMap({
                        columns: columns,
                        title: '平均',
                        checks: statistics.average,
                        method: (column) => XEUtils.mean(data, column.property)
                    }))
                }
            }
            return footers
        },

        getFooterStatisticsMap({columns, title, checks, method}) {
            return columns.map((column, columnIndex) => {
                if (columnIndex === 0) {
                    return title
                }
                if (checks.includes(column.property)) {
                    return method(column, columnIndex)
                }
                return null
            })
        },

        /** 表尾单元格合并方法 */
        handleFooterSpanMethod(event) {
            if (event.columnIndex === 0) {
                return {colspan: 2}
            }
        },

        /*--- 外部可调用接口方法 ---*/

        /**
         * 重置滚动条Top位置
         * @param top 新top位置，留空则滚动到上次记录的位置，用于解决切换tab选项卡时导致白屏以及自动将滚动条滚动到顶部的问题
         */
        resetScrollTop(top) {
            this.scrollTo(null, (top == null || top === '') ? this.scroll.top : top)
        },

        /**
         * 加载新数据，和 loadData 不同的是，用该方法加载的数据都是相当于点新增按钮新增的数据。
         * 适用于不是数据库里查出来的没有id的临时数据
         * @param dataSource
         */
        async loadNewData(dataSource) {
            if (Array.isArray(dataSource)) {
                let {xTable} = this.$refs.vxe.$refs
                // issues/2784
                // 先清空所有数据
                xTable.loadData([])

                dataSource.forEach((data, idx) => {
                    // 开启了排序就自动计算排序值
                    if (this.dragSort) {
                        this.$set(data, this.dragSortKey, idx + 1)
                    }
                    // 处理联动回显数据
                    if (this._innerLinkageConfig != null) {
                        for (let configItem of this._innerLinkageConfig.values()) {
                            this.autoSetLinkageOptionsByData(data, '', configItem, 0)
                        }
                    }
                })
                // 再新增
                return xTable.insertAt(dataSource)
            }
            return []
        },

        // 校验table，失败返回errMap，成功返回null
        async validateTable() {
            const errMap = await this.validate().catch(errMap => errMap)
            return errMap ? errMap : null
        },
        // 完整校验
        async fullValidateTable() {
            const errMap = await this.fullValidate().catch(errMap => errMap)
            return errMap ? errMap : null
        },

        /** 设置某行某列的值 */
        setValues(values) {
            if (!Array.isArray(values)) {
                console.warn(`JVxeTable.setValues：必须传递数组`)
                return
            }
            values.forEach((item, idx) => {
                let {rowKey, values: record} = item
                let {row} = this.getIfRowById(rowKey)
                if (!row) {
                    return
                }
                Object.keys(record).forEach(colKey => {
                    let column = this.getColumnByKey(colKey)
                    if (column) {
                        let oldValue = row[colKey]
                        let newValue = record[colKey]
                        if (newValue !== oldValue) {
                            this.$set(row, colKey, newValue)
                            // 触发 valueChange 事件
                            this.trigger('valueChange', {
                                type: column.own.$type,
                                value: newValue,
                                oldValue: oldValue,
                                col: column.own,
                                column: column,
                                isSetValues: true,
                            })
                        }
                    } else {
                        console.warn(`JVxeTable.setValues：没有找到key为"${colKey}"的列`)
                    }
                })
            })
        },

        /** 获取所有的数据，包括values、deleteIds */
        getAll() {
            return {
                tableData: this.getTableData(),
                deleteData: this.getDeleteData()
            }
        },
        /** 获取表格表单里的值 */
        getValues(callback, rowIds) {
            let tableData = this.getTableData({rowIds: rowIds})
            callback('', tableData)
        },
        /** 获取表格数据 */
        getTableData(options = {}) {
            let {rowIds} = options
            let tableData
            // 仅查询指定id的行
            if (Array.isArray(rowIds) && rowIds.length > 0) {
                tableData = []
                rowIds.forEach(rowId => {
                    let {row} = this.getIfRowById(rowId)
                    if (row) {
                        tableData.push(row)
                    }
                })
            } else {
                // 查询所有行
                tableData = this.$refs.vxe.getTableData().fullData
            }
            return this.filterNewRows(tableData, false)
        },
        /** 仅获取新增的数据 */
        getNewData() {
            let newData = cloneObject(this.$refs.vxe.getInsertRecords())
            newData.forEach(row => delete row.id)
            return newData
        },
        /** 仅获取新增的数据,带有id */
        getNewDataWithId() {
            let newData = cloneObject(this.$refs.vxe.getInsertRecords())
            return newData
        },
        /** 根据ID获取行，新增的行也能查出来 */
        getIfRowById(id) {
            let row = this.getRowById(id), isNew = false
            if (!row) {
                row = this.getNewRowById(id)
                if (!row) {
                    console.warn(`JVxeTable.getIfRowById：没有找到id为"${id}"的行`)
                    return {row: null}
                }
                isNew = true
            }
            return {row, isNew}
        },
        /** 通过临时ID获取新增的行 */
        getNewRowById(id) {
            let records = this.getInsertRecords()
            for (let record of records) {
                if (record.id === id) {
                    return record
                }
            }
            return null
        },
        /** 仅获取被删除的数据（新增又被删除的数据不会被获取到） */
        getDeleteData() {
            return cloneObject(this.$refs.vxe.getRemoveRecords())
        },
        /**
         * 添加一行或多行
         *
         * @param rows
         * @param isOnlJs 是否是onlineJS增强触发的
         * @return
         */
        async addRows(rows = {}, isOnlJs) {
            return this._addOrInsert(rows, -1, 'added', isOnlJs)
        },

        /**
         * 添加一行或多行
         *
         * @param rows
         * @param index 添加下标，数字，必填
         * @return
         */
        async insertRows(rows, index) {
            if (typeof index !== 'number' || index < 0) {
                console.warn(`【JVXETable】insertRows：index必须传递数字，且大于-1`)
                return
            }
            return this._addOrInsert(rows, index, 'inserted')
        },
        /**
         * 添加一行或多行临时数据，不会填充默认值，传什么就添加进去什么
         * @param rows
         * @param options 选项
         * @param options.setActive 是否激活最后一行的编辑模式
         */
        async pushRows(rows = {}, options = {}) {
            let {xTable} = this.$refs.vxe.$refs
            let {setActive, index} = options
            setActive = setActive == null ? false : !!setActive
            index = index == null ? -1 : index
            index = index === -1 ? index : xTable.tableFullData[index]
            // 插入行
            let result = await xTable.insertAt(rows, index)
            if (setActive) {
                // 激活最后一行的编辑模式
                xTable.setActiveRow(result.rows[result.rows.length - 1])
            }
            await this._recalcSortNumber()
            return result
        },

        /** 清空选择行 */
        clearSelection() {
            let event = {$table: this.$refs.vxe, target: this}
            if (this.rowSelectionType === JVXETypes.rowRadio) {
                this.$refs.vxe.clearRadioRow()
                this.handleVxeRadioChange(event)
            } else {
                this.$refs.vxe.clearCheckboxRow()
                this.handleVxeCheckboxChange(event)
            }
        },

        /** 删除一行或多行数据 */
        async removeRows(rows) {
            const res = await this._remove(rows)
            await this._recalcSortNumber()
            return res
        },

        /** 根据id删除一行或多行 */
        removeRowsById(rowId) {
            let rowIds
            if (Array.isArray(rowId)) {
                rowIds = rowId
            } else {
                rowIds = [rowId]
            }
            let rows = rowIds.map((id) => {
                let {row} = this.getIfRowById(id)
                if (!row) {
                    return
                }
                if (row) {
                    return row
                } else {
                    console.warn(`【JVXETable】removeRowsById：${id}不存在`)
                    return null
                }
            }).filter((row) => row != null)
            return this.removeRows(rows)
        },

        getColumnByKey() {
            return this.$refs.vxe.getColumnByField.apply(this.$refs.vxe, arguments)
        },

        /* --- 辅助方法 ---*/

        // 触发事件
        trigger(name, event = {}) {
            event.$target = this
            event.$table = this.$refs.vxe
            //online增强参数兼容
            event.target = this
            this.$emit(name, event)
        },

        /** 【多级联动】获取同级联动下拉选项 */
        getLinkageOptionsSibling(row, col, config, request) {
            // 如果当前列不是顶级列
            let key = ''
            if (col.key !== config.key) {
                // 就找出联动上级列
                let idx = config.keys.findIndex(k => col.key === k)
                let parentKey = config.keys[idx - 1]
                key = row[parentKey]
                // 如果联动上级列没有选择数据，就直接返回空数组
                if (key === '' || key == null) {
                    return []
                }
            } else {
                key = 'root'
            }
            let options = config.optionsMap.get(key)
            if (!Array.isArray(options)) {
                if (request) {
                    let parent = key === 'root' ? '' : key
                    return this.getLinkageOptionsAsync(config, parent)
                } else {
                    options = []
                }
            }
            return options
        },
        /** 【多级联动】获取联动下拉选项（异步） */
        getLinkageOptionsAsync(config, parent) {
            return new Promise(resolve => {
                let key = parent ? parent : 'root'
                let options
                if (config.optionsMap.has(key)) {
                    options = config.optionsMap.get(key)
                    if (options instanceof Promise) {
                        options.then(opt => {
                            config.optionsMap.set(key, opt)
                            resolve(opt)
                        })
                    } else {
                        resolve(options)
                    }
                } else if (typeof config.requestData === 'function') {
                    // 调用requestData方法，通过传入parent来获取子级
                    let promise = config.requestData(parent)
                    config.optionsMap.set(key, promise)
                    promise.then(opt => {
                        config.optionsMap.set(key, opt)
                        resolve(opt)
                    })
                } else {
                    resolve([])
                }
            })
        },
        // 【多级联动】 用于回显数据，自动填充 optionsMap
        autoSetLinkageOptionsByData(data, parent, config, level) {
            if (level === 0) {
                this.getLinkageOptionsAsync(config, '')
            } else {
                this.getLinkageOptionsAsync(config, parent)
            }
            if (config.keys.length - 1 > level) {
                let value = data[config.keys[level]]
                if (value) {
                    this.autoSetLinkageOptionsByData(data, value, config, level + 1)
                }
            }
        },
        // 【多级联动】联动组件change时，清空下级组件
        linkageSelectChange(row, col, config, value) {
            if (col.linkageKey) {
                this.getLinkageOptionsAsync(config, value)
                let idx = config.keys.findIndex(k => k === col.key)
                let values = {}
                for (let i = idx; i < config.keys.length; i++) {
                    values[config.keys[i]] = ''
                }
                // 清空后几列的数据
                this.setValues([{rowKey: row.id, values}])
            }
        },

        /** 加载数据字典并合并到 options */
        _loadDictConcatToOptions(column) {
            initDictOptions(column.dictCode).then((res) => {
                if (res.success) {
                    let newOptions = (column.options || [])// .concat(res.result)
                    res.result.forEach(item => {
                        // 过滤重复数据
                        for (let option of newOptions) if (option.value === item.value) return
                        newOptions.push(item)
                    })
                    this.$set(column, 'options', newOptions)
                } else {
                    console.group(`JVxeTable 查询字典(${column.dictCode})发生异常`)
                    console.warn(res.message)
                    console.groupEnd()
                }
            })
        },
        //options自定义赋值 刷新
        virtualRefresh() {
            this.scrolling = true
            this.closeScrolling()
        },
        // 设置 this.scrolling 防抖模式
        closeScrolling: simpleDebounce(function () {
            this.scrolling = false
        }, 100),

        /**
         * 过滤添加的行
         * @param rows 要筛选的行数据
         * @param remove true = 删除新增，false=只删除id
         * @param handler function
         */
        filterNewRows(rows, remove = true, handler) {
            let insertRecords = this.$refs.vxe.getInsertRecords()
            let records = []
            for (let row of rows) {
                let item = cloneObject(row)
                if (insertRecords.includes(row)) {
                    handler ? handler({item, row, insertRecords}) : null

                    if (remove) {
                        continue
                    }
                    delete item.id
                }
                records.push(item)
            }
            return records
        },

        // 删除选中的数据
        async removeSelection() {
            let res = await this._remove(this.selectedRows)
            this.clearSelection()
            await this._recalcSortNumber()
            return res
        },

        /**
         * 【删除指定行数据】（重写vxeTable的内部方法，添加了从keepSource中删除）
         * 如果传 row 则删除一行
         * 如果传 rows 则删除多行
         * 如果为空则删除所有
         */
        _remove(rows) {
            const xTable = this.$refs.vxe.$refs.xTable

            const {
                afterFullData,
                tableFullData,
                tableSourceData,
                editStore,
                treeConfig,
                checkboxOpts,
                selection,
                isInsertByRow,
                scrollYLoad
            } = xTable
            const {actived, removeList, insertList} = editStore
            const {checkField: property} = checkboxOpts
            let rest = []
            const nowData = afterFullData
            if (treeConfig) {
                throw new Error(UtilTools.getLog('vxe.error.noTree', ['remove']))
            }
            if (!rows) {
                rows = tableFullData
            } else if (!XEUtils.isArray(rows)) {
                rows = [rows]
            }
            // 如果是新增，则保存记录
            rows.forEach(row => {
                if (!isInsertByRow(row)) {
                    removeList.push(row)
                }
            })
            // 如果绑定了多选属性，则更新状态
            if (!property) {
                XEUtils.remove(selection, row => rows.indexOf(row) > -1)
            }
            // 从数据源中移除
            if (tableFullData === rows) {
                rows = rest = tableFullData.slice(0)
                tableFullData.length = 0
                nowData.length = 0
            } else {
                rest = XEUtils.remove(tableFullData, row => rows.indexOf(row) > -1)
                XEUtils.remove(nowData, row => rows.indexOf(row) > -1)
            }
            // 【从keepSource中删除】
            if (xTable.keepSource) {
                let rowIdSet = new Set(rows.map(row => row.id))
                XEUtils.remove(tableSourceData, row => rowIdSet.has(row.id))
            }

            // 如果当前行被激活编辑，则清除激活状态
            if (actived.row && rows.indexOf(actived.row) > -1) {
                xTable.clearActived()
            }
            // 从新增中移除已删除的数据
            XEUtils.remove(insertList, row => rows.indexOf(row) > -1)
            xTable.handleTableData()
            xTable.updateFooter()
            xTable.updateCache()
            xTable.checkSelectionStatus()
            if (scrollYLoad) {
                xTable.updateScrollYSpace()
            }
            return xTable.$nextTick().then(() => {
                xTable.recalculate()
                return {row: rest.length ? rest[rest.length - 1] : null, rows: rest}
            })
        },

        /** 行重新排序 */
        async rowResort(oldIndex, newIndex) {
            const xTable = this.$refs.vxe.$refs.xTable
            window.xTable = xTable
            const sort = (array) => {
                // 存储旧数据，并删除旧项目
                let row = array.splice(oldIndex, 1)[0]
                // 向新项目里添加旧数据
                array.splice(newIndex, 0, row)
            }
            sort(xTable.tableFullData)
            if (xTable.keepSource) {
                sort(xTable.tableSourceData)
            }
            await this.$nextTick()
            await this._recalcSortNumber()
        },

        /** 重新计算排序字段的数值 */
        async _recalcSortNumber() {
            const xTable = this.$refs.vxe.$refs.xTable
            if (this.dragSort) {
                xTable.tableFullData.forEach((data, idx) => data[this.dragSortKey] = (idx + 1))
            }
            await xTable.updateCache(true)
            return await xTable.updateData()
        },

        async _addOrInsert(rows = {}, index, triggerName, isOnlJs) {
            let {xTable} = this.$refs.vxe.$refs
            let records
            if (Array.isArray(rows)) {
                records = rows
            } else {
                records = [rows]
            }
            // 遍历添加默认值
            records.forEach(record => this._createRow(record))
            let result = await this.pushRows(records, {index: index, setActive: true})
            // 遍历插入的行
            // update--begin--autor:lvdandan-----date:20201117------for:LOWCOD-987 【新行编辑】js增强附表内置方法调用问题 #1819
            // online js增强时以传过来值为准，不再赋默认值
            if (isOnlJs != true) {
                for (let i = 0; i < result.rows.length; i++) {
                    let row = result.rows[i]
                    this.trigger(triggerName, {
                        row: row,
                        $table: xTable,
                        target: this,
                    })
                }
            }
            // update--end--autor:lvdandan-----date:20201117------for:LOWCOD-987 【新行编辑】js增强附表内置方法调用问题 #1819
            return result
        },
        // 创建新行，自动添加默认值
        _createRow(record = {}) {
            let {xTable} = this.$refs.vxe.$refs
            // 添加默认值
            xTable.tableFullColumn.forEach(column => {
                let col = column.own
                if (col.key && (record[col.key] == null || record[col.key] === '')) {
                    // 设置默认值
                    let createValue = getEnhancedMixins(col.$type || col.type, 'createValue')
                    record[col.key] = createValue({row: record, column, $table: xTable})
                }
                // update-begin--author:sunjianlei---date:20210819------for: 处理联动列，联动列只能作用于 select 组件
                if (col.$type === JVXETypes.select && this._innerLinkageConfig != null) {
                    // 判断当前列是否是联动列
                    if (this._innerLinkageConfig.has(col.key)) {
                        let configItem = this._innerLinkageConfig.get(col.key)
                        this.getLinkageOptionsAsync(configItem, '')
                    }
                }
                // update-end--author:sunjianlei---date:20210819------for: 处理联动列，联动列只能作用于 select 组件
            })
            return record
        },

        /*--- 渲染函数 ---*/

        // 渲染 vxe
        renderVxeGrid(h) {
            return h('vxe-grid', {
                ref: 'vxe',
                class: ['j-vxe-table'],
                props: this.vxeProps,
                on: this.vxeEvents,
                // 作用域插槽的格式为
                scopedSlots: this.$scopedSlots,
            })
        },
        // 渲染工具栏
        renderToolbar(h) {
            if (this.toolbar) {
                return h('j-vxe-toolbar', {
                    props: {
                        toolbarConfig: this.toolbarConfig,
                        excludeCode: this.excludeCode,
                        size: this.size,
                        disabled: this.disabled,
                        disabledRows: this.disabledRows,
                        selectedRowIds: this.selectedRowIds,
                    },
                    on: {
                        // 新增事件
                        add: () => this.addRows(),
                        // 保存事件
                        save: () => this.trigger('save', {
                            $table: this.$refs.vxe,
                            target: this,
                        }),
                        // 删除事件
                        remove: () => {
                            let $table = this.$refs.vxe
                            let deleteRows = this.filterNewRows(this.selectedRows)
                            // 触发删除事件
                            if (deleteRows.length > 0) {
                                let removeEvent = {deleteRows, $table, target: this}
                                if (this.asyncRemove) {
                                    // 确认删除，只有调用这个方法才会真删除
                                    removeEvent.confirmRemove = () => this.removeSelection()
                                } else {
                                    this.removeSelection()
                                }
                                this.trigger('remove', removeEvent)
                            } else {
                                this.removeSelection()
                            }
                        },
                        // 清除选择事件
                        clearSelection: this.clearSelection
                    },
                    scopedSlots: {
                        toolbarPrefix: this.$scopedSlots.toolbarPrefix,
                        toolbarSuffix: this.$scopedSlots.toolbarSuffix,
                    },
                })
            }
            return null
        },
        // 渲染 toolbarAfter 插槽
        renderToolbarAfterSlot() {
            if (this.$scopedSlots['toolbarAfter']) {
                return this.$scopedSlots['toolbarAfter']()
            }
            return null
        },
        // 渲染点击时弹出的子表
        renderSubPopover(h) {
            if (this.clickRowShowSubForm && this.$scopedSlots.subForm) {
                return h('j-vxe-sub-popover', {
                    ref: 'subPopover',
                    scopedSlots: {
                        subForm: this.$scopedSlots.subForm,
                    }
                })
            }
            return null
        },
        // 渲染点击时弹出的详细信息
        renderDetailsModal(h) {
            if (this.clickRowShowMainForm && this.$scopedSlots.mainForm) {
                return h('j-vxe-details-modal', {
                    ref: 'detailsModal',
                    scopedSlots: {
                        subForm: this.clickRowShowSubForm ? this.$scopedSlots.subForm : null,
                        mainForm: this.$scopedSlots.mainForm
                    }
                })
            }
        },
        // 渲染分页器
        renderPagination(h) {
            if (this.pagination && Object.keys(this.pagination).length > 0) {
                return h('j-vxe-pagination', {
                    props: {
                        size: this.size,
                        disabled: this.disabled,
                        pagination: this.pagination
                    },
                    on: {
                        change: (e) => this.trigger('pageChange', e)
                    },
                })
            }
            return null
        },
        loadExcludeCode() {
            if (!this.authPre || this.authPre.length == 0) {
                this.excludeCode = []
            } else {
                let pre = this.authPre
                if (!pre.endsWith(':')) {
                    pre += ':'
                }
                this.excludeCode = getNoAuthCols(pre)
            }
        }

    },
    render(h) {
        return h('div', {
            class: ['j-vxe-table-box', `size--${this.size}`]
        }, [
            this.renderSubPopover(h),
            this.renderDetailsModal(h),
            this.renderToolbar(h),
            this.renderToolbarAfterSlot(),
            this.renderVxeGrid(h),
            this.renderPagination(h),
        ])
    },
    beforeDestroy() {
        this.$emit('beforeDestroy')
    }
}

// 兼容 online 的规则
const fooPatterns = [
    {title: '非空', value: '*', pattern: /^.+$/},
    {title: '6到16位数字', value: 'n6-16', pattern: /^\d{6,16}$/},
    {title: '6到16位任意字符', value: '*6-16', pattern: /^.{6,16}$/},
    {title: '6到18位字母', value: 's6-18', pattern: /^[a-z|A-Z]{6,18}$/},
    {
        title: '网址',
        value: 'url',
        pattern: /^(?:([A-Za-z]+):)?(\/{0,3})([0-9.\-A-Za-z]+)(?::(\d+))?(?:\/([^?#]*))?(?:\?([^#]*))?(?:#(.*))?$/
    },
    {title: '电子邮件', value: 'e', pattern: /^([\w]+\.*)([\w]+)@[\w]+\.\w{3}(\.\w{2}|)$/},
    {title: '手机号码', value: 'm', pattern: /^1[3456789]\d{9}$/},
    {title: '邮政编码', value: 'p', pattern: /^[1-9]\d{5}$/},
    {title: '字母', value: 's', pattern: /^[A-Z|a-z]+$/},
    {title: '数字', value: 'n', pattern: /^-?\d+(\.?\d+|\d?)$/},
    {title: '整数', value: 'z', pattern: /^-?\d+$/},
    {title: '金额', value: 'money', pattern: /^(([1-9][0-9]*)|([0]\.\d{0,2}|[1-9][0-9]*\.\d{0,2}))$/},
]

/** 旧版handler转为新版Validator */
function handlerConvertToValidator(event) {
    const {column, rule} = event
    return new Promise((resolve, reject) => {
        rule.handler(event, (flag, msg) => {
            let message = rule.message
            if (typeof msg === 'string') {
                message = replaceProps(column.own, msg)
            }
            if (flag == null) {
                resolve(message)
            } else if (!!flag) {
                resolve(message)
            } else {
                reject(new Error(message))
            }
        }, this, event)
    })
}

/** 唯一校验器 */
function uniqueValidator(event) {
    const {cellValue, column, rule} = event
    let tableData = this.getTableData()
    let findCount = 0
    for (let rowData of tableData) {
        if (rowData[column.own.key] === cellValue) {
            if (++findCount >= 2) {
                return Promise.reject(new Error(rule.message))
            }
        }
    }
    return Promise.resolve()
}